﻿using Newtonsoft.Json;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SHDM.Cache
{
    public class RedisCacheManager : ICacheManager
    {
        #region Fields
        private readonly string _connectionString;
        private static readonly object _Locker = new object();
        Lazy<ConnectionMultiplexer> lazyConnection = null;
        private Lazy<ConnectionMultiplexer> LazyConnection
        {
            get
            {
                if (lazyConnection == null)
                {
                    lock (_Locker)
                    {
                        int count = 0;
                        createconn:
                        count++;
                        if (lazyConnection == null && count < 4)//连续获取3次
                        {
                            try
                            {
                                lazyConnection = new Lazy<ConnectionMultiplexer>(() =>
                                {
                                    return ConnectionMultiplexer.Connect(_connectionString);
                                });
                            }
                            catch (Exception ex)
                            {
                                lazyConnection = null;
                                goto createconn;
                            }
                        }
                    }
                }
                return lazyConnection;

            }
        }

        IDatabase _Database
        {
            get
            {
                return Muxer.GetDatabase(Conf.RedisDatabase);
            }
        }
        
        ConnectionMultiplexer Muxer
        {
            get
            {
                try
                {
                    return LazyConnection.Value;
                }
                catch (Exception ex)
                {
                    lazyConnection = null;
                }
                return null;
            }
        }
        #endregion

        #region Ctor
        public RedisCacheManager(string connectionString)
        {
            if (String.IsNullOrEmpty(connectionString))
                throw new Exception("Redis connection string is empty");           
            _connectionString = connectionString;
            var t = LazyConnection;
            //this._perRequestCacheManager = EngineContext.Current.ContainerManager.Resolve<ICacheManager>(CacheHelper.CacheType.PER_REQUEST_CACHE);
        }

        #endregion

        #region Methods
        public void Clear()
        {
            foreach (var ep in Muxer.GetEndPoints())
            {
                var server = Muxer.GetServer(ep);
                var keys = server.Keys();
                var _db = this._Database;
                foreach (var key in keys)
                    _db.KeyDelete(key);
            }
        }

        public void Dispose()
        {
            if (Muxer != null)
                Muxer.Dispose();
        }

        public T Get<T>(string key)
        {
            var rValue = _Database.StringGet(key);
            if (!rValue.HasValue)
                return default(T);
            var result = Deserialize<T>(rValue);
            
            return result;
        }

        public bool IsSet(string key)
        {
            return _Database.KeyExists(key);
        }

        public void Remove(string key)
        {
            _Database.KeyDelete(key);
        }

        public void RemoveByPattern(string pattern)
        {
            foreach (var ep in Muxer.GetEndPoints())
            {
                var server = Muxer.GetServer(ep);
                var keys = server.Keys(pattern: "*" + pattern + "*");
                var _db = this._Database;
                foreach (var key in keys)
                    _db.KeyDelete(key);
            }
        }

        public void Set(string key, object data, int cacheTime)
        {
            if (data == null)
                return;

            var entryBytes = Serialize(data);
            var expiresIn = TimeSpan.FromMinutes(cacheTime);
            _Database.StringSet(key, entryBytes, expiresIn);
            //_Database.StringSet(key, entryBytes);
        }

        #endregion

        #region Utilities
        protected virtual byte[] Serialize(object item)
        {
            var jsonString = JsonConvert.SerializeObject(item);
            return Encoding.UTF8.GetBytes(jsonString);
        }

        protected virtual T Deserialize<T>(byte[] serializedObject)
        {
            if (serializedObject == null)
                return default(T);

            var jsonString = Encoding.UTF8.GetString(serializedObject);
            return JsonConvert.DeserializeObject<T>(jsonString);
        }
        #endregion
    }
}
